接下來要來完成我們網站的導覽部分了,這裡阿森主要分為兩個大項目,分別是Navbar和Sidebar。Navbar就是一般看網頁時會浮在最上面的導覽選單,而Sidebar則是使用手機或是小視窗打開時,會縮小成一個按鈕,要打開才能做選擇的導覽選單。
今天會先講Navbar的設計,那我們就開始吧!
有看前幾篇的figma設計篇的話,應該都看過我設計的草稿了,這裡我想做的是當訪客點開網頁時,出現的第一個畫面是這個:
當他把滑鼠滾輪往下捲時,我的Navbar會從上面跑下來,同時底部也會有一個Slogan往上跑,變成這樣:
Logo也會漸漸往左上角移動,同時縮小一點。
再來是如果訪客是用手機或是小視窗打開時:
會讓他自動縮小,至於點開Sidebar的部分就留到明天介紹。
這裡阿森是用前面提到過的架構來建立我react的資料夾,所以一共有這些內容:
那我們最主要的Home也是放在pages這個資料夾中的index.js裡,先把我們的HeroSection加上來:
index.js in pages:
import React from 'react'
import HeroSection from '../components/HeroSection'
import { useState } from 'react'
const Home = () => {
const [isOpen, setIsOpen] = useState(false)
const toggle = () => {
setIsOpen(!isOpen)
}
return (
<>
<HeroSection toggle = { toggle }/>
//傳入一個toggle讓HeroSection裡面的MobileIcon可以偵測之後Sidebar的開關。
</>
)
}
export default Home
接著我們在components裡面新增一個HeroSection資料夾,裡面有一個index.js、style.css還有一個HeroElements.js,因為這裡我們會用到前面提到的style component:
為了達成scroll的效果,我們要用到css和javascript的搭配,所以index.js和style.css都完成後會長這樣:
index.js
import { MobileIcon, Logo} from './HeroElements'
import logo from '../../images/logo.png'
import back from '../../images/back.jpg'
import name from '../../images/name.png'
import './style.css'
import {FaBars} from 'react-icons/fa'
const HeroSection = ({toggle}) => {
window.addEventListener("scroll", ()=>{
const header = document.querySelector('header');
header.classList.toggle('sticky', window.scrollY > 0);
});
return (
<>
<img className="logo" src = {logo} />
<header id="header">
<img src= {back} style={{opacity: 1}} alt="background" className = "banner"/>
<Logo className="name" src = {name}/>
<nav>
<ul>
<li><img className="navLogo" src={logo}></img></li>
<li><a href ="/" style={{background:"gray", borderRadius:"10px"}}>News</a></li>
<li><a href ="/">Intro</a></li>
<li><a href ="/">Roadmap</a></li>
<li><a href ="/">Buy</a></li>
<li><a href ="/">FAQs</a></li>
<li><a href ="/">Team</a></li>
<MobileIcon onClick = {toggle} >
<FaBars />
</MobileIcon>
</ul>
</nav>
<div className="bottom"><p>What if their legends never end.</p></div>
</header>
</>
)
}
export default HeroSection
style.css
body{
min-height: 1000px;
background: black;
}
header {
position: relative;
top: 0;
left: 0;
width: 100%;
height: 100vh;
background-origin: #000;
display: flex;
justify-content: flex-end;
align-items: center;
transition: 1s;
}
header .banner{
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
object-fit: cover;
transition: 1s;
}
header.sticky nav{
flex: none;
background: black;
width: 100%;
transition: 1s;
top: 0;
}
header nav {
flex: none;
transition: 1s;
width: 100%;
top: -80px;
}
header .name{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 200px;
width: auto;
z-index: 10;
transition: 1s;
}
header.sticky .name{
position: absolute;
font-size: 2em;
left: 50px;
top: 30%;
transform: translate(0, -50%);
width: auto;
height: 150px;
transition: 1s;
}
@media screen and (max-width: 768px){
header.sticky .name{
position: absolute;
transform: translate(0, -50%);
height: 75px;
width: auto;
transition: 1s;
}
header .name{
height: 75px;
width: auto;
}
header .banner{
max-width: auto;
height: 100vh;
}
.bottom{
width: fit-content;
}
nav ul li a{
color: #fff;
display: none !important;
}
.navLogo{
margin-left: -70px;
}
.bottom p{
margin-left: 50%;
font-size: 0.5em;
}
header.sticky .navLogo{
margin-left: -200%;
}
}
nav {
position: fixed;
display: flex;
z-index: 10;
padding-right: 5%;
top: 0;
}
nav ul{
position: relative;
display: flex;
flex: none;
flex-direction: row;
transition: 0.25s;
opacity: 0;
justify-content: space-evenly;
width: 100%;
}
header.sticky nav ul{
opacity: 1;
transition-delay: 0.75s;
align-items: center;
}
nav ul li{
list-style: none;
}
nav ul li a{
color: #000;
display: flex;
padding: 10px 10px;
font-size: 1.3em;
text-decoration: none;
color: white;
top: 0;
font-weight: 700;
}
nav ul li a:hover{
color: #c64c4c;
}
nav ul li img{
transition: 0.5s;
}
nav ul li img:hover{
transform: scale(1.5);
transition: 0.5s;
cursor: pointer;
}
.navLogo{
height: 35px;
width: auto;
}
.bottom{
width: 100%;
height: 60px;
position: absolute;
display: flex;
bottom: -60px;
z-index: 5;
background: #000;
color: #fff;
font-size: 1.8em;
font-weight: 600;
justify-content: center;
align-items: center;
transition: 1s ;
}
.bottom p{
margin-left: 50%;
transition: 1s;
transition-delay: 0.75s ;
}
header.sticky .bottom{
display: flex;
bottom: 0;
transition: 1s ;
}
.logo{
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 200px;
z-index: 1;
opacity: 0.7;
}
header.sticky.logo{
position: fixed;
opacity: 1;
}
主要是設定header在偵測到scroll這個動作後,會觸發.sticky這個class,所以在css檔案中,可以用header.sticky這個選擇器來選擇捲動後的各個tag,之後再加上transition: 1s; 這項指令就可以達到緩緩移動的效果了。
那這裡要注意@media,他可以控制在手機螢幕還有網頁寬度小於768px時內容的表現方法,而為了達到我們的目的,這裡要把Nav ul li a的display設成none才行,然後我們來完成HeroElements.js:
這裡我們設定MobileIcon在小於768px時才會顯現,而這個Icon我們會從react-icon這個插件中使用FaBars來完成,也就是在index.js中import的那個。關於react插件安裝的部分也可以看前面的react介紹篇哦!
所以在完成這些之後,最重要的就是把圖片存到images這個資料夾中,像是這樣:
最後阿森附上我的package給大家參考:
{
"name": "dino",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"gsap": "^3.7.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-gsap": "^3.2.1",
"react-icons": "^4.2.0",
"react-router-dom": "^5.2.1",
"react-scripts": "4.0.3",
"react-scroll": "^1.8.4",
"react-scrollmagic": "^2.3.0",
"styled-components": "^5.3.1",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
就可以達成我們要的效果啦!
那Navbar的部分就先到這邊,明天再來延伸Sidebar要怎麼和他結合在一起吧!